9. Testing

Automated testing is seen as a key part of Grails, implemented using Groovy Tests. Hence, Grails provides many ways to making testing easier from low level unit testing to high level functional tests. This section details the different capabilities that Grails offers in terms of testing.

The first thing to be aware of is that all of the create-* commands actually end up creating integration tests automatically for you. For example say you run the create-controller command as follows:

grails create-controller simple

Not only will Grails create a controller at grails-app/controllers/SimpleController.groovy, but also an integration test at test/integration/SimpleControllerTests.groovy. What Grails won't do however is populate the logic inside the test! That is left up to you.

Once you have done this, you can then execute all the tests with the test-app command:

grails test-app

The above command will produce output such as:

-------------------------------------------------------
Running Unit Tests…
Running test FooTests...FAILURE
Unit Tests Completed in 464ms …
-------------------------------------------------------

Tests failed: 0 errors, 1 failures

Whilst reports will have been written out the test/reports directory. You can also run an individual test by specifying the name of the test (without the Tests suffix) to run:

grails test-app SimpleController

In addition, you can run a number of tests by specifying each ones name separated by a space:

grails test-app SimpleController BookController

9.1 Unit Testing

Unit testing are tests at the "unit" level. In other words you are testing individual methods or blocks of code without considering for surrounding infrastructure. In Grails you need to be particularity aware of the difference between unit and integration tests because in unit tests Grails does not inject any of the dynamic methods present during integration tests and at runtime.

The reason for this is that it is left up to you to mock these methods using something like Groovy Mock or ExpandoMetaClass.

For example say you have an action like the following in BookController:

def show = {
    [ book : Book.get( params.id ) ]
}

The params object and the get method are both provided at runtime by Grails and not available in the unit test, but that can be mocked out. To do this using ExpandoMetaClass you could do as follows:

void testShow() {
	// mock the static get method
	Book.metaClass.static.get = { Long id -> 
		assert id == 10
		new Book(id:id,title:"The Stand")
    } 
    // mock the params object	
	BookController.metaClass.getParams = {-> [id:10] }
	def controller = new BookController()
	def model = controller.show()
	assert model
	assert model.book
	assertEquals 10, model.book.id
	assertEquals "The Stand", model.book.title	
}

Notice how we provide a custom implementation of the get method that returns a mocked instance and how we can even use assertions within this implementation. Also note how we provide a mocked instance of the params object using a map.

9.2 Integration Testing

Integration tests differ from unit tests in that you have full access to the Grails environment within the test. Grails will use an in-memory HSQLDB database for integration tests and clear out all the data from the database in between each test.

Testing Controllers

To test controllers you first have to understand the Spring Mock Library

Essentially Grails automatically configures each test with a MockHttpServletRequest, MockHttpServletResponse, and MockHttpSession which you can then use to perform your tests. For example consider the following controller:

class FooController {

def text = { render "bar" }

def someRedirect = { redirect(action:"bar") } }

The tests for this would be:

class FooControllerTests extends GroovyTestCase {

void testText() { def fc = new FooController() fc.text() assertEquals "bar", fc.response.contentAsString }

void testSomeRedirect() {

def fc = new FooController() fc.someRedirect() assertEquals "/foo/bar", fc.response.redirectedUrl } }

In the above case the response is an instance of MockHttpServletResponse which we can use to obtain the contentAsString (when writing to the response) or the URL redirected to for example. These mocked versions of the Servlet API are, unlike the real versions, all completely mutable and hence you can set properties on the request such as the contextPath and so on.

Grails does not invoke interceptors automatically when calling actions during integration testing. You should test interceptors in isolation, and via functional testing if necessary.

Testing Controllers with Services

If your controller references a service, you have to explicitly initialise the service from your test.

Given a controller using a service:

class FilmStarsController {
    def popularityService

def update = { // do something with popularityService } }

The test for this would be:

class FilmStarsTests extends GroovyTestCase {
    def popularityService

public void testInjectedServiceInController () { def fsc = new FilmStarsController() fsc.popularityService = popularityService fsc.update() } }

Testing Controller Command Objects

With command objects you just supply parameters to the request and it will automatically do the command object work for you when you call your action with no parameters:

Given a controller using a command object:

class AuthenticationController {
    def signup = { SignupForm form ->
        …
    }
}

You can then test it like this:

def controller = new AuthenticationController()
controller.params.login = "marcpalmer"
controller.params.password = "secret"
controller.params.passwordConfirm = "secret"
controller.signup()

Grails auto-magically sees your call to signup() as a call to the action and populates the command object from the mocked request parameters. During controller testing, the params are mutable with a mocked request supplied by Grails.

Testing Controllers and the render Method

The render method allows you to render a custom view at any point within the body of an action. For instance, consider the example below:

def save = {
	def book = Book(params)
	if(book.save()) {
		// handle
	}
	else {
		render(view:"create", model:[book:book])
	}
}

In the above example the result of the model of the action is not available as the return value, but instead is stored within the modelAndView property of the controller. The modelAndView property is an instance of Spring MVC's ModelAndView class and you can use it to the test the result of an action:

def bookController = new BookController()
bookController.save()
def model = bookController.modelAndView.model.book

Simulating Request Data

If you're testing an action that requires request data such as a REST web service you can use the Spring MockHttpServletRequest object to do so. For example consider this action which performs data binding from an incoming request:

def create = {
	[book: new Book(params['book']) ]	
}

If you wish the simulate the 'book' parameter as an XML request you could do something like the following:

void testCreateWithXML() {
	def controller = new BookController()
	controller.request.contentType = 'text/xml'
	controller.request.contents = '''<?xml version="1.0" encoding="ISO-8859-1"?>
	<book>
		<title>The Stand</title>
		…
	</book>	
	'''.getBytes() // note we need the bytes

def model = controller.create() assert model.book assertEquals "The Stand", model.book.title }

The same can be achieved with a JSON request:

void testCreateWithJSON() {
	def controller = new BookController()	
 	controller.request.contentType = "text/json"
 	controller.request.content = '{"id":1,"class":"Book","title":"The Stand"}'.getBytes()

def model = controller.create() assert model.book assertEquals "The Stand", model.book.title

}

With JSON don't forget the class property to specify the name the target type to bind too. In the XML this is implicit within the name of the <book> node, but with JSON you need this property as part of the JSON packet.

For more information on the subject of REST web services see the section on REST.

Testing Web Flows

Testing Web Flows requires a special test harness called grails.test.WebFlowTestCase which sub classes Spring Web Flow's AbstractFlowExecutionTests class.

Subclasses of WebFlowTestCase must be integration tests

For example given this trivial flow:

class ExampleController {
	def exampleFlow = {
		start {
			on("go") {
				flow.hello = "world"
			}.to "next"
		}
		next {
			on("back").to "start"
			on("go").to "end"
		}
		end()
	}	
}

You need to tell the test harness what to use for the "flow definition". This is done via overriding the abstract getFlow method:

class ExampleFlowTests extends grails.test.WebFlowTestCase {
	def getFlow() { new ExampleController().exampleFlow }
	…
}

If you need to specify the flow id you can do so by overriding the getFlowId method otherwise the default is test:

class ExampleFlowTests extends grails.test.WebFlowTestCase {
	String getFlowId() { "example" }
	…
}

Once this is done in your test you need to kick off the flow with the startFlow method which returns a ViewSelection object:

void testExampleFlow() {
	def viewSelection = startFlow()

assertEquals "start", viewSelection.viewName … }

As demonstrated above you can check you're on the right state using the viewName property of the ViewSelection object. To trigger and event you need to use the signalEvent method:

void testExampleFlow() {
	…
	viewSelection = signalEvent("go")
	assertEquals "next", viewSelection.viewName
	assertEquals "world", viewSelection.model.hello
}

Here we have signaled to the flow to execute the event "go" this causes a transition to the "next" state. In the example a transition action placed a hello variable into the flow scope. We can test the value of this variable by inspecting the model property of the ViewSelection as above.

Testing Tag Libraries

Testing tag libraries is actually pretty trivial because when a tag is invoked as a method it returns its result as a string. So for example if you have a tag library like this:

class FooTagLib {
   def bar =  { attrs, body ->
   	   out << "<p>Hello World!</p>"
   }

def bodyTag = { attrs, body -> out << "<${attrs.name}>" out << body() out << "</${attrs.name}>" } }

The tests would look like:

class FooTagLibTests extends GroovyTestCase {

void testBarTag() { assertEquals "<p>Hello World!</p>", new FooTagLib().bar(null,null) }

void testBodyTag() { assertEquals "<p>Hello World!</p>", new FooTagLib().bodyTag(name:"p") { "Hello World!" } } }

Notice that for the second example, testBodyTag, we pass a block that returns the body of the tag. This is handy for representing the body as a String.

Testing Tag Libraries with GroovyPagesTestCase

In addition to doing simply testing of tag libraries like the above you can also use the grails.test.GroovyPagesTestCase class to test tag libraries.

The GroovyPagesTestCase class is a sub class of the regular GroovyTestCase class and provides utility methods for testing the output of a GSP rendering.

GroovyPagesTestCase can only be used in an integration test.

As an example given a date formatting tag library such as the one below:

class FormatTagLib {
	def dateFormat = { attrs, body -> 
		out << new java.text.SimpleDateFormat(attrs.format) << attrs.date
	}
}

This can be easily tested as follows:

class FormatTagLibTests extends GroovyPagesTestCase {
	void testDateFormat() {
		def template = '<g:dateFormat format="dd-MM-yyyy" date="${myDate}" />'

def testDate = … // create the date assertOutputEquals( '01-01-2008', template, [myDate:testDate] ) } }

You can also obtain the result of a GSP using the applyTemplate method of the GroovyPagesTestCase class:

class FormatTagLibTests extends GroovyPagesTestCase {
	void testDateFormat() {
		def template = '<g:dateFormat format="dd-MM-yyyy" date="${myDate}" />'

def testDate = … // create the date def result = applyTemplate( template, [myDate:testDate] )

assertEquals '01-01-2008', result } }

Testing Domain Classes

Testing domain classes is typically a simple matter of using the GORM API, however there are some things to be aware of. Firstly, if you are testing queries you will often need to "flush" in order to ensure the correct state has been persisted to the database. For example take the following example:

void testQuery() {
	def books = [ new Book(title:"The Stand"), new Book(title:"The Shining")]
	books*.save()

assertEquals 2, Book.list().size() }

This test will actually fail, because calling save does not actually persist the Book instances when called. Calling save merely indicates to Hibernate that at some point in the future these instances should be persisted. If you wish to commit changes immediately you need to "flush" them:

void testQuery() {
	def books = [ new Book(title:"The Stand"), new Book(title:"The Shining")]
	books*.save(flush:true)

assertEquals 2, Book.list().size() }

In this case since we're passing the argument flush with a value of true the updates will be persisted immediately and hence will be available to the query later on.

9.3 Functional Testing

Functional tests involve testing the actual running application and are often harder to automate. Grails does not ship with any functional testing support out of the box, but has support for Canoo WebTest via a plug-in.

To get started install Web Test with the following commands:

grails install-plugin webtest

Then refer to the reference on the wiki which explains how to go about using Web Test and Grails.